package socofwebget;

import java.util.ArrayList;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import java.io.PrintWriter;
import org.apache.derby.drda.NetworkServerControl;

// PARA O MAIN:
//        System.out.println("DEMO DO DERBY:");
//        System.out.println("http://svn.apache.org/repos/asf/db/derby/code/trunk/java/demo/simple/SimpleApp.java");
//
//        System.out.println("Inicio");
//
//        System.out.println("Vamos agora testar o DBClienteServidor");
//        DBClienteServidor ligacao= new DBClienteServidor();
//        try {
//            ligacao.init();
//        } catch (Exception ex) {
//            Logger.getLogger(SocofWebGet.class.getName()).log(Level.SEVERE, null, ex);
//        }
//        ligacao.connection();
//    try {
//        Thread.sleep(1000);
//    } catch (InterruptedException ex) {
//        Logger.getLogger(SocofWebGet.class.getName()).log(Level.SEVERE, null, ex);
//    }
//        ligacao.shutdown();
//
//    try {
//        Thread.sleep(1000);
//    } catch (InterruptedException ex) {
//        Logger.getLogger(SocofWebGet.class.getName()).log(Level.SEVERE, null, ex);
//    }
//
//
// ARRANCAR SERVIDOR LINHA DE COMANDOS
//$ java -jar derbyrun.jar server start
//Security manager installed using the Basic server security policy.
//Apache Derby Network Server - 10.4.1.3 - (648739) started and ready to
//	accept connections on port 1527 at 2008-04-30 09:35:55.871 GMT
// Para tentar arrancar automaticamente (INICIO)
//http://stackoverflow.com/questions/3507019/automatically-start-javadb-derby-on-glassfish
//private NetworkServerControl networkServerControlImpl;
//private String driver = "org.apache.derby.jdbc.ClientDriver";
public class DBClienteServidor {

    private String protocol = "jdbc:derby://localhost:1527/";
    private String dbName = "socofWebGet";
    private String driver = "org.apache.derby.jdbc.ClientDriver";

// Para arrancar automaticamente
    public void init() {
        System.out.println("http://db.apache.org/derby/docs/10.0/manuals/admin/hubprnt14.html");
        // PARA TENTAR ARRANQUE AUTOMATICO
        //

        //Registar o driver (Até que ponto é necessário?):
        try {
            DriverManager.registerDriver(new org.apache.derby.jdbc.ClientDriver());

            PrintWriter saida = new PrintWriter(System.out, true);
            NetworkServerControl server = new NetworkServerControl();
            server.start(saida);
            //    } catch (SQLException se){
        } catch (Exception e) {
            System.err.println(e.toString());
        }



        try {
            Class.forName(driver).newInstance();
        } catch (java.lang.ClassNotFoundException e) {
            System.err.print("ClassNotFoundException: ");
            System.err.println(e.getMessage());
        } catch (Exception e) {
            System.out.println("Erro ao arrancar o ClientDriver");
            return;
        }
    }

    public Connection connection() {
        Connection conn = null;

//        try {
//            Class.forName(driver);
//        } catch (Exception e) {
//            System.out.println("Erro ao arrancar o ClientDriver");
//            return;
//        }
        try {
            conn = DriverManager.getConnection(protocol + dbName, "SOCOF", "socof");

//DEBBUG
//            Statement select = conn.createStatement();
//            ResultSet result = select.executeQuery("SELECT FICHEIRO, ESTADO from SOCOF.\"FICHEIROS\"");
//
//            System.out.println("Got results:");
//            while (result.next()) { // process results one row at a time
//                String FICHEIRO = result.getString(1);
//                String ESTADO = result.getString(2);
//
//                System.out.println("FICHEIRO = " + FICHEIRO);
//                System.out.println("ESTADO = " + ESTADO);
//            }
//            select.close();
//            conn.close();
        } catch (SQLException se) {

            if (se.getErrorCode() == 40000 && ("XJ040".equals(se.getSQLState()))) {
                System.out.println("Base de dados ja arrancada 1");
                conn = connection();
                return conn;
            } else if (se.getErrorCode() == 45000 && ("XSDB6".equals(se.getSQLState()))) {
                System.out.println("Base de dados ja arrancada 2");
                conn = connection();
                return conn;
            } else {
                // if the error code or SQLState is different, we have
                // an unexpected exception (shutdown failed)
                System.err.println("Derby did not shut down normally");
                printSQLException(se);
            }
        }
        return conn;

    }

// PARA DESLIGAR BD - É boa pratica e torna o arranque mais rapido
// uma vez que assim o derby não tem que fazer verificações no arranque
    public void shutdown() {
        try {
            //Desliga So a nossa BD:
            //DriverManager.getConnection(protocol + dbName +";shutdown=true", "SOCOF", "socof");
            //Desliga TODAS AS BD
            //DriverManager.getConnection(protocol + ";shutdown=true");
            DriverManager.getConnection(protocol + dbName + ";shutdown=true", "SOCOF", "socof");
        } //catch (SQLException ex) {
        //            Logger.getLogger(dbDerbyEmbedded.class.getName()).log(Level.SEVERE, null, ex);
        //        }
        catch (SQLException se) {
            if (((se.getErrorCode() == -1)
                    && ("08006".equals(se.getSQLState())))) {
                // we got the expected exception
                System.out.println("Derby shut down normally - ------ VERIFICAR OS CODIGOS DE EXCEPCAO");
                // Note that for Embedded shutdown, the expected
                // SQL state is "XJ015", and the error code is 50000.
            } else {
                // if the error code or SQLState is different, we have
                // an unexpected exception (shutdown failed)
                System.err.println("Derby did not shut down normally");
                printSQLException(se);
            }
        }
    }
// Para apagar tabelas. Ja apanha erros e tudo

    public boolean deleteTable(String table) {
        boolean resultado = false;
        try {
            Connection ligacao = connection();
            Statement deletion = ligacao.createStatement();
            resultado = deletion.execute("DROP TABLE " + table.toUpperCase());
            deletion.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42Y55".equals(se.getSQLState()))) {
                System.out.println("Erro ao apagar: Tabela " + table.toUpperCase() + " nao existente");
            } else {
                System.err.println("Erro inesperado ao apagar a tabela");
                printSQLException(se);
            }
        }
        return resultado;
    }

// Para fazer um select recebido por argumento. Ja apanha erros e tudo
    public void sqlSelect(String sqlCommand) {

        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery(sqlCommand);

            System.out.println("SQLSelect   :");
            while (result.next()) { // process results one row at a time
                String FICHEIRO = result.getString("FICHEIRO");
                String ESTADO = result.getString("ESTADO");

                System.out.println("FICHEIRO = " + FICHEIRO);
                System.out.println("ESTADO = " + ESTADO);
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.out.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }
    }

// Para criar tabela com estrutura. Ja apanha erros e tudo
// Recebe no 1o argumento nome da tabela
// Recebe no 2o argumento string de criacao das colunas
    public boolean createTable(String table, String columns) {
        boolean resultado = false;
        String tabela = table.toUpperCase();
        String create = "CREATE TABLE \"" + tabela + "\" (" + columns + ")";
//        System.out.println("Vou criar o seguinte: " + create);
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Logger.getLogger(SocofWebGet.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            Connection ligacao = connection();
            Statement creation = ligacao.createStatement();
            resultado = creation.execute(create);

            creation.close();
            ligacao.close();
        } catch (SQLException se) {
            se.printStackTrace();
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        return resultado;
    }

// Ja insere e trata erros conforme
    private int sqlFxInsert(Ficheiro dados) throws SQLException {
        boolean resultadoExecucao = false;
        int idResultante = -1;
        Connection ligacao = connection();
        String sqlInsertCommand = "Insert into \"FICHEIROS\" ("
                + "\"DOMINIO\", "
                + "\"FICHEIRO\", "
                + "\"ESTADO\", "
                + "\"TAMANHOSITE\", "
                + "\"CAMINHOSITE\", "
                + "\"CAMINHOLOCAL\", "
                + "\"TIPO\", "
                + "\"NIVEIS\", "
                + "\"ACTUALIZADO\") values ('"
                + dados.getDominio().toLowerCase() + "', '" + dados.getFicheiro() + "', '"
                + dados.getEstado().toUpperCase() + "', '" + dados.getTamanhosite() + "', '"
                + dados.getCaminhosite() + "', '" + dados.getCaminholocal() + "', '"
                + dados.getTipo().toUpperCase() + "', " + dados.getNiveis() + ", CURRENT DATE)";
        //                dados.getTipo()+", "+dados.getNiveis()+", '"+dados.getActualizado()

        Statement insert = ligacao.createStatement();


        try {
            insert.executeUpdate(sqlInsertCommand, Statement.RETURN_GENERATED_KEYS);

            ResultSet resInsert = insert.getGeneratedKeys();
            resInsert.next();
            System.err.println("Apos insercao ficou com ID: " + resInsert.getInt(1));
            idResultante = resInsert.getInt(1);
            resultadoExecucao = true;
        } catch (SQLException se) {
            if (((se.getErrorCode() == 30000)
                    && ("23505".equals(se.getSQLState())))) {
                System.err.println("Registo duplicado. Nao foi possivel inserir.");
            } else {
                System.err.println("Outro erro ao inserir registo");
                printSQLException(se);
            }
        }
        if (!resultadoExecucao) {
            System.err.println("Erro ao Inserir................ ");
            return idResultante;
        }
        System.out.println("Inserido com sucesso!!!!!!!!!!!!!!");
        return idResultante;
    }

//Se nao existir adiciona, se existir, actualiza
//EM QQE um dos casos, devolvera o id
    public int writeData(Ficheiro dados) {
        System.out.println("Recebi fx com id: " + dados.getId());
        int resultado = -1;
        // Se nao existir, adiciona
        if ((dados.getId() == null) || (!sqlExisteId(dados))) {
            System.out.println("Vou Inserir");
            try {
                resultado = sqlFxInsert(dados);
            } catch (SQLException ex) {
                printSQLException(ex);
            }
        } // Se existir, actualiza
        else {
            try {
                System.out.println("Vou actualizar");
                resultado = sqlFxUpdate(dados);
            } catch (SQLException ex) {
                printSQLException(ex);
            }
        }
        return resultado;
    }

// Para actualizar existentes
    private int sqlFxUpdate(Ficheiro dados) throws SQLException {
        boolean resultadoExecucao = false;
        int idExecutado = -1;
        Connection ligacao = connection();
        String sqlUpdateCommand = "UPDATE \"FICHEIROS\" set "
                + "\"DOMINIO\"= '" + dados.getDominio().toLowerCase()
                + "',\"FICHEIRO\"= '" + dados.getFicheiro()
                + "',\"ESTADO\"= '" + dados.getEstado().toUpperCase()
                + "',\"TAMANHOSITE\"= '" + dados.getTamanhosite()
                + "',\"CAMINHOSITE\"= '" + dados.getCaminhosite()
                + "',\"CAMINHOLOCAL\"= '" + dados.getCaminholocal()
                + "',\"TIPO\"= '" + dados.getTipo().toUpperCase()
                + "',\"NIVEIS\"= " + dados.getNiveis()
                + ",\"ACTUALIZADO\"= CURRENT DATE "
                + "where \"ID\"=" + dados.getId();
        System.out.println(sqlUpdateCommand);
        Statement update = ligacao.createStatement();

        try {
            update.executeUpdate(sqlUpdateCommand, Statement.RETURN_GENERATED_KEYS);

//DEBUG
//            ResultSet resInsert = update.getGeneratedKeys();
//            resInsert.next();
//            System.err.println("Apos Actualizacao ficou com ID: " + resInsert.getInt(1));
//            idExecutado=resInsert.getInt(1);
            resultadoExecucao = true;
            idExecutado = dados.getId();
        } catch (SQLException se) {
            if (((se.getErrorCode() == 30000)
                    && ("23505".equals(se.getSQLState())))) {
                System.err.println("Registo duplicado. Nao actualizei.");
            } else {
                System.err.println("Outro erro ao actualizar registo");
                printSQLException(se);
            }
        }
        if (!resultadoExecucao) {
            System.err.println("Erro ao actualizar valores................ ");
            return idExecutado;
        }
        System.out.println("Actualizado com sucesso!!!!!!!!!!!!!!");
        return idExecutado;
    }

// Para saber se já existe o ficheiro passando objecto
    public boolean sqlExisteId(Ficheiro dados) {
        boolean existencia = false;
        System.err.println("Vou testar se existe o ID " + dados.getId());
        if (dados.getId() == null) {
            System.err.println("Recebi um in null em :" + dados.getFicheiro());
            return existencia;
        }
        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery("SELECT ID FROM \"FICHEIROS\" WHERE \"ID\"="
                    + dados.getId());

            // System.out.println("TESTE PARA LISTAR TUDO E VER SE EXISTE ID");
            while (result.next()) { // processa resultados, um a um
                // System.out.println("Encontrado registo existente com ID: " + result.getInt(1));
                if (result.getInt(1) > 0) {
                    existencia = true;
                }
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.out.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }
        return existencia;
    }
// Para saber se já existe o ficheiro passando o ID

    public boolean sqlExisteId(int idFicheiro) {
        boolean existencia = false;
        System.out.println("Vou testar se existe por ID: " + idFicheiro);

        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery("SELECT ID FROM \"FICHEIROS\" WHERE \"ID\"="
                    + idFicheiro);

            //System.out.println("TESTE PARA LISTAR TUDO E VER SE EXISTE ID");
            while (result.next()) { // processa resultados, um a um
                //System.out.println("Encontrado registo existente com ID: " + result.getInt(1));
                if (result.getInt(1) > 0) {
                    existencia = true;
                }
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.err.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }
        return existencia;
    }

// Para devolver Array com lista de todos os FX do dominio
    public ArrayList<Ficheiro> getByDomain(String dominio) {
        ArrayList<Ficheiro> lista = new ArrayList<Ficheiro>();

        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery("SELECT * from \"FICHEIROS\""
                    + "where \"DOMINIO\" = '" + dominio + "'");


            System.out.println("Get tudo do dominio :" + dominio);
            while (result.next()) { // process results one row at a time
                Ficheiro fx = new Ficheiro(
                        result.getInt("ID"),
                        result.getString("DOMINIO"),
                        result.getString("FICHEIRO"),
                        result.getString("ESTADO"),
                        result.getLong("TAMANHOSITE"),
                        result.getString("CAMINHOSITE"),
                        result.getString("CAMINHOLOCAL"),
                        result.getString("TIPO"),
                        result.getInt("NIVEIS"),
                        result.getDate("ACTUALIZADO"));
                lista.add(fx);
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.out.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }

        return lista;
    }

// Para devolver Array com lista de todos os fx INCOMPLETO do dominio
    public ArrayList<Ficheiro> getIncompleteByDomain(String dominio) {
        ArrayList<Ficheiro> lista = new ArrayList<Ficheiro>();
        String estado = "INCOMPLETO";

        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery("SELECT * from \"FICHEIROS\""
                    + "where \"DOMINIO\" = '" + dominio + "' and ESTADO = '"
                    + estado.toUpperCase() + "'");


            System.out.println("Get " + estado + " do dominio :" + dominio);
            while (result.next()) { // process results one row at a time
                Ficheiro fx = new Ficheiro(
                        result.getInt("ID"),
                        result.getString("DOMINIO"),
                        result.getString("FICHEIRO"),
                        result.getString("ESTADO"),
                        result.getLong("TAMANHOSITE"),
                        result.getString("CAMINHOSITE"),
                        result.getString("CAMINHOLOCAL"),
                        result.getString("TIPO"),
                        result.getInt("NIVEIS"),
                        result.getDate("ACTUALIZADO"));
                lista.add(fx);
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.out.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }

        return lista;
    }

// Para devolver Array com lista de todos os fx COMPLETO do dominio
    public ArrayList<Ficheiro> getCompleteByDomain(String dominio) {
        ArrayList<Ficheiro> lista = new ArrayList<Ficheiro>();
        String estado = "COMPLETO";

        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery("SELECT * from \"FICHEIROS\""
                    + "where \"DOMINIO\" = '" + dominio + "' and ESTADO = '"
                    + estado.toUpperCase() + "'");


            System.out.println("Get " + estado + " do dominio :" + dominio);
            while (result.next()) { // process results one row at a time
                Ficheiro fx = new Ficheiro(
                        result.getInt("ID"),
                        result.getString("DOMINIO"),
                        result.getString("FICHEIRO"),
                        result.getString("ESTADO"),
                        result.getLong("TAMANHOSITE"),
                        result.getString("CAMINHOSITE"),
                        result.getString("CAMINHOLOCAL"),
                        result.getString("TIPO"),
                        result.getInt("NIVEIS"),
                        result.getDate("ACTUALIZADO"));
                lista.add(fx);
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.out.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }

        return lista;
    }

// Para devolver Array com lista de todos os fx do dominio conforme ESTADO passado
    public ArrayList<Ficheiro> getByDomainStatus(String dominio, String status) {
        ArrayList<Ficheiro> lista = new ArrayList<Ficheiro>();
        String estado = status.toUpperCase();

        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery("SELECT * from \"FICHEIROS\""
                    + "where \"DOMINIO\" = '" + dominio + "' and ESTADO = '"
                    + estado + "'");


            System.out.println("Get " + estado + " do dominio :" + dominio);
            while (result.next()) { // process results one row at a time
                Ficheiro fx = new Ficheiro(
                        result.getInt("ID"),
                        result.getString("DOMINIO"),
                        result.getString("FICHEIRO"),
                        result.getString("ESTADO"),
                        result.getLong("TAMANHOSITE"),
                        result.getString("CAMINHOSITE"),
                        result.getString("CAMINHOLOCAL"),
                        result.getString("TIPO"),
                        result.getInt("NIVEIS"),
                        result.getDate("ACTUALIZADO"));
                lista.add(fx);
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.out.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }

        return lista;
    }

// Devolve um unico ficheiro conforme o id
    public Ficheiro getFicheiro(int id) {
        Ficheiro fx = null;

        try {
            Connection ligacao = connection();
            Statement select = ligacao.createStatement();
            ResultSet result = select.executeQuery("SELECT * from \"FICHEIROS\""
                    + "where \"ID\" = " + id + "");


            System.out.println("Get tudo do Ficheiro com id :" + id);
            while (result.next()) { // process results one row at a time
                fx = new Ficheiro(
                        result.getInt("ID"),
                        result.getString("DOMINIO"),
                        result.getString("FICHEIRO"),
                        result.getString("ESTADO"),
                        result.getLong("TAMANHOSITE"),
                        result.getString("CAMINHOSITE"),
                        result.getString("CAMINHOLOCAL"),
                        result.getString("TIPO"),
                        result.getInt("NIVEIS"),
                        result.getDate("ACTUALIZADO"));
            }
            select.close();
            ligacao.close();
        } catch (SQLException se) {
            if ((se.getErrorCode() == 30000) && ("42X05".equals(se.getSQLState()))) {
                System.out.println("Erro ao fazer SELECT: Tabela nao existente.");
            } else {
                System.err.println("Erro inesperado ao fazer SELECT.");
                printSQLException(se);
            }
        }

        return fx;
    }

////////////////////////////////////
//  EM INVESTIGACAO A PARTIR DAQUI
////////////////////////////////////
    // Devolver Ficheiro por estado por dominio devolve string com caminho completo
    // Devolver id
    // Devolver URL por id
    // Actualizar estado por id
    //
    // FAZER LOCKS A BD PARA TODAS AS OPERACOES DE ESCRITA
    // http://db.apache.org/derby/docs/10.0/manuals/develop/develop75.html#HDRSII-CONCEPTS-16400
    // Devolver Ficheiro por estado por dominio devolve string com caminho completo
    public void sqlAlter(String sqlCommand) throws SQLException {

        Connection ligacao = connection();
        Statement alter = ligacao.createStatement();
        ResultSet result = alter.executeQuery(sqlCommand);

        System.out.println("Got results from Embedded   :");
        while (result.next()) { // process results one row at a time
            String FICHEIRO = result.getString(1);
            String ESTADO = result.getString(2);

            System.out.println("FICHEIRO = " + FICHEIRO);
            System.out.println("ESTADO = " + ESTADO);
        }
        alter.close();
        ligacao.close();
    }

    // Para tratar de excepcoes do SQL.
    public static void printSQLException(SQLException e) {
        // Unwraps the entire exception chain to unveil the real cause of the
        // Exception.
        while (e != null) {
            System.err.println("\n----- SQLException -----");
            System.err.println("  SQL State:  " + e.getSQLState());
            System.err.println("  Error Code: " + e.getErrorCode());
            System.err.println("  Message:    " + e.getMessage());
            // for stack traces, refer to derby.log or uncomment this:
            //e.printStackTrace(System.err);
            e = e.getNextException();
        }
    }
}
